package dbPhase.hypeerweb;

//import org.sqlite.*;
import java.sql.*;
import java.util.HashSet;
import java.util.Iterator;

/**
 * HyPeerWebDatabase handles all the operations of saving an existing HyPeerWeb to the database
 * so that it can be repopulated and used at a later time.
 *
 * @pre Domain: <br>
 * 		DATABASE_DIRECTORY = "db/"
 * 		DEFAULT_DATABASE_NAME = "HyPeerWeb.db"
 * 		HyPeerWebDatabase singleton = null
 * 		databaseName
 * 		conn - java.sql.Connection
 *
 *
 */
public class HyPeerWebDatabase {

	public static final String DATABASE_DIRECTORY = "db/";
	public static final String DEFAULT_DATABASE_NAME = "HyPeerWeb.db";
	private static HyPeerWebDatabase singleton = null;
	public String databaseName;
	public java.sql.Connection conn;

	/**
	 * saveNodes Does a clean save of the nodes into the database
	 *
	 * @pre nodes is a proper set of simplified nodes that came from
	 * the HyPeerWeb that is being saved.
	 *
	 * @param nodes - a hash set of nodes that have been turned into simplifed nodes.
	 */
	public void saveNodes(HashSet<SimplifiedNodeDomain> nodes) {
		clearDatabase();

		Iterator<SimplifiedNodeDomain> it = nodes.iterator();

		while (it.hasNext()) {
			SimplifiedNodeDomain node = it.next();
			saveNode(node);
		}
	}

	/**
	 * getNode returns a node from a given webId from the database
	 *
	 * @pre a int > 0
	 * @post the node from the database or null if it doesn't exist
	 *
	 * @param webID - an integer representation of the wanted node.
	 * @return the node from the database or null if it doesn't exist
	 */
	public SimplifiedNodeDomain getNode(int webID) {
		if (conn == null) {
			System.out.println("conn is null??");
			return null;
		}
		try {
			if (!checkNodeExist(webID)) {
				return null;
			}
			int height = getHeight(webID);
			HashSet<Integer> neighbors = getNeighbors(webID);
			HashSet<Integer> upPointers = getUpPointers(webID);
			HashSet<Integer> downPointers = getDownPointers(webID);
			int fold = getFold(webID);
			int s_fold = getSurrogateFold(webID);
			int inv_s_fold = getInverseSurrogateFold(webID);
			int state = getState(webID);

			return new SimplifiedNodeDomain(webID, height, neighbors,
					upPointers, downPointers, fold, s_fold, inv_s_fold, state);

		}
		catch (Exception e) {
			return null;
		}
	}

	/**
	 * getState - returns from the database the state that the node was in when it was saved.
	 *
	 * @pre webID is > 0
	 * @param webID - the integer representation of the wanted Node to return its state
	 *
	 * @return -1 if not found otherwise returns the state of the node.
	 */
	private int getState(int webID) {
		try {
			String query = "SELECT fold FROM nodeInfo WHERE node = "
				+ webID;
			Statement statement = conn.createStatement();
			ResultSet info = statement.executeQuery(query);

			if (isResultSetEmpty(info, "state")) {
				return -1;
			}

		//	return -1;
//			System.out.println("THE FOLD= "+info.getInt("fold") );
			return info.getInt("state");
		}
		catch (Exception e) {
			System.out.println("Get state failed");
			return -1;
		}
	}

	/**
	 * Get the singleton
	 */
	public static HyPeerWebDatabase getSingleton() {
		if (singleton == null) {
			initHyPeerWebDatabase("");
		}
		return singleton;
	}


	public static void initHyPeerWebDatabase() {
		String dbName = DEFAULT_DATABASE_NAME;
	try {
		if (singleton != null) {
			singleton.conn.close();
		}
	}
	catch (Exception e) {
		System.out.println(e);
	}
	singleton = new HyPeerWebDatabase(dbName);
	}
	/**
	 * Initiate the singleton with a database name
	 */
	static void initHyPeerWebDatabase(String dbName) {
		if (dbName == null || dbName == "") {
			dbName = DEFAULT_DATABASE_NAME;
		}
		try {
			if (singleton != null) {
				singleton.conn.close();
			}
		}
		catch (Exception e) {
			System.out.println(e);
		}
		singleton = new HyPeerWebDatabase(dbName);
	}

	/**
	 * clearDatabase Clears the database
	 *
	 * @pre none
	 */
	public void clearDatabase() {
		try {
			String query = "delete from ";
			Statement statement = conn.createStatement();
			statement.executeUpdate(query + "nodeInfo");
			statement.executeUpdate(query + "nodeNeighborRelation");
			statement.executeUpdate(query + "nodeFoldRelation");
			statement.close();
		}
		catch (Exception e) {
			System.out.println("DB not cleared");
			System.out.println(e);
		}
	}

	public void clear() {
		clearDatabase();
	}

	/**
	 * Private methods
	 */
	private HyPeerWebDatabase(String dbName) {
		conn = null;
		String user = "root";
		String password = "root";
//		String driver = "com.mysql.jdbc.Driver";

		try {
			  Class.forName("org.sqlite.JDBC"); //.newInstance();
			  conn = DriverManager.getConnection("jdbc:sqlite:"
					  + dbName, user, password);



			  System.out.println("Connected to the database");
		}
		catch (Exception e) {
			  e.printStackTrace();
	    }
	}

	private void saveNode(SimplifiedNodeDomain node) {
		//Statement statement = null;
		int webId = node.getWebId();
//		System.out.println("Node To be Saved: " + node.getWebId()); //zzzzz
//		System.out.println("fold: " + node.fold);
//		System.out.println("height: " + node.height);
//		System.out.println("inverseSurrogateFold: " + node.inverseSurrogateFold);
//		System.out.println("surrogateFold: " + node.surrogateFold);

		try {
			String query = "insert into nodeInfo values("
				+ webId + "," + node.height + "," + node.state + ")";
			Statement statement = conn.createStatement();
			statement.executeUpdate(query);

			for (Integer i: node.neighbors) {
				query = "insert into nodeNeighborRelation values("
					+ webId + "," + i + ",0)";
				statement = conn.createStatement();
				statement.executeUpdate(query);
			}

			for (Integer i: node.downPointers) {
				query = "insert into nodeNeighborRelation values("
					+ webId + "," + i + ",1)";
				statement = conn.createStatement();
				statement.executeUpdate(query);
			}

			if (node.fold != -1) {
				query = "insert into nodeFoldRelation values("
					+ webId + "," + node.fold + ",0)";
				statement = conn.createStatement();
				statement.executeUpdate(query);
			}
			if (node.surrogateFold != -1) {

				query = "insert into nodeFoldRelation values("
					+ webId + "," + node.surrogateFold + ",1)";
//				System.out.println(query);
				statement = conn.createStatement();
				statement.executeUpdate(query);
			}



		//	System.out.println("Size of Database: " + statement.getFetchSize()); //zzzzz
		}
		catch (Exception e) {
			System.out.println(e); //zzzzz
			System.out.println("ADD TO DATABASE FAILED"); //zzzzz
		}
	}

	private boolean checkNodeExist(int webId) {
		try {
			String query = "SELECT height FROM nodeInfo WHERE webId = " + webId;
			Statement statement = conn.createStatement();
			ResultSet info = statement.executeQuery(query);


			return !isResultSetEmpty(info, "height");
		}
		catch (Exception e) {
			System.out.println("Inexistant: " + webId); //zzzzz
			return false;
		}
	}

	private int getHeight(int webId) {
		try {
			String query = "SELECT height FROM nodeInfo WHERE webId = " + webId;
			Statement statement = conn.createStatement();
			ResultSet info = statement.executeQuery(query);

			if (isResultSetEmpty(info, "height")) {
				return -1;
			}

			return info.getInt("height");
		}
		catch (Exception e) {
			System.out.println("Select failed for " + webId);
			System.out.println(e.getMessage());
			return -1;
		}
	}

	private HashSet<Integer> getNeighbors(int webId) {
		try {
			String query = "SELECT neighbor FROM nodeNeighborRelation WHERE node = "
				+ webId + " and isSurrogate = 0";
			Statement statement = conn.createStatement();
			ResultSet info = statement.executeQuery(query);


			if (isResultSetEmpty(info, "neighbor")) {
				return new HashSet<Integer>();
			}

			HashSet<Integer> retv = new HashSet<Integer>();

			while (info.next()) {
				//System.out.println("another neighbor");
				int n = info.getInt("neighbor");
				retv.add(Integer.valueOf(n));
			}
			return retv;
		}
		catch (Exception e) {
			return new HashSet<Integer>();
		}
	}

	private HashSet<Integer> getDownPointers(int webId) {
		try {
			String query = "SELECT * FROM nodeNeighborRelation WHERE node = "
				+ webId + " and isSurrogate = 1";
			Statement statement = conn.createStatement();
			ResultSet info = statement.executeQuery(query);

			if (isResultSetEmpty(info, "neighbor")) {
				return new HashSet<Integer>();
			}

			HashSet<Integer> retv = new HashSet<Integer>();

			while (info.next()) {
				int n = info.getInt("neighbor");
				retv.add(Integer.valueOf(n));
			}

		//		System.out.println("WebId: " + webId + " down: " + retv.toString());
			return retv;
		}
		catch (Exception e) {
			return new HashSet<Integer>();
		}
	}

	private HashSet<Integer> getUpPointers(int webId) {
		try {
			String query = "SELECT * FROM nodeNeighborRelation WHERE neighbor = "
				+ webId + " and isSurrogate = 1";
			Statement statement = conn.createStatement();
			ResultSet info = statement.executeQuery(query);

			if (isResultSetEmpty(info, "node")) {
				return new HashSet<Integer>();
			}

			HashSet<Integer> retv = new HashSet<Integer>();

			while (info.next()) {
				int n = info.getInt("node");
				retv.add(Integer.valueOf(n));
			}
			return retv;
		}
		catch (Exception e) {
			return new HashSet<Integer>();
		}
	}

	private int getFold(int webId) {
		try {
			String query = "SELECT fold FROM nodeFoldRelation WHERE node = "
				+ webId + " and isSurrogate = 0";
			Statement statement = conn.createStatement();
			ResultSet info = statement.executeQuery(query);

			if (isResultSetEmpty(info, "fold")) {
				return -1;
			}

		//	return -1;
//			System.out.println("THE FOLD= "+info.getInt("fold") );
			return info.getInt("fold");
		}
		catch (Exception e) {
			System.out.println("Get fold failed");
			return -1;
		}
	}

	private int getSurrogateFold(int webId) {
		try {
			String query = "SELECT fold FROM nodeFoldRelation WHERE node = "
				+ webId + " and isSurrogate = 1";
			Statement statement = conn.createStatement();
			ResultSet info = statement.executeQuery(query);

			if (isResultSetEmpty(info, "fold")) {
				return -1;
			}

//			System.out.println("THE SURROGATEFOLD= "+info.getInt("fold") );
			return info.getInt("fold");
		}
		catch (Exception e) {
			System.out.println("Get fold failed");
			return -1;
		}
	}

	private int getInverseSurrogateFold(int webId) {
		try {
			String query = "SELECT * FROM nodeFoldRelation WHERE fold = "
				+ webId + " and isSurrogate = 1";
			Statement statement = conn.createStatement();
			ResultSet info = statement.executeQuery(query);

			if (isResultSetEmpty(info, "node")) {
				return -1;
			}

			return info.getInt("node");
		}
		catch (Exception e) {
			return -1;
		}
	}

	private boolean isResultSetEmpty(ResultSet rs, String s) {
		try {
			rs.getString(s);
			return false;
		}
		catch (Exception e) {
			//System.out.println("AAAUGH!");
			return true;
		}
	}
}
